home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / rpg / crossfir.92 / crossfir / crossfire-0.92.5 / common / button.c < prev    next >
C/C++ Source or Header  |  1996-07-24  |  15KB  |  537 lines

  1. /*
  2.  * static char *rcsid_button_c =
  3.  *   "$Id: button.c,v 1.27 1996/07/24 07:00:18 master Exp master $";
  4.  */
  5.  
  6. /*
  7.     CrossFire, A Multiplayer game for X-windows
  8.  
  9.     Copyright (C) 1992 Frank Tore Johansen
  10.  
  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License as published by
  13.     the Free Software Foundation; either version 2 of the License, or
  14.     (at your option) any later version.
  15.  
  16.     This program is distributed in the hope that it will be useful,
  17.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.     GNU General Public License for more details.
  20.  
  21.     You should have received a copy of the GNU General Public License
  22.     along with this program; if not, write to the Free Software
  23.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25.     The author can be reached via e-mail to frankj@ifi.uio.no.
  26. */
  27.  
  28. #include <global.h>
  29. #include <funcpoint.h>
  30.  
  31. /*
  32.  * This code is no longer highly inefficient 8)
  33.  */
  34.  
  35. /*
  36.  * Push the specified object.  This can affect other buttons/gates/handles
  37.  * altars/pedestals/holes in the whole map.
  38.  * Changed the routine to loop through _all_ objects.
  39.  * Better hurry with that linked list...
  40.  */
  41.  
  42. void push_button(object *op) {
  43.   object *tmp;
  44.   objectlink *ol;
  45.  
  46. /*   LOG(llevDebug, "push_button: %s (%d)\n", op->name, op->count); */
  47.   for (ol = get_button_links(op); ol; ol = ol->next) {
  48.     if (!ol->ob || ol->ob->count != ol->id) {
  49.       LOG(llevError, "Internal error in push_button.\n");
  50.       continue;
  51.     }
  52.     /* a button link object can become freed when the map is saving.  As
  53.      * a map is saved, objects are removed and freed, and if an object is
  54.      * on top of a button, this function is eventually called.  If a map
  55.      * is getting moved out of memory, the status of buttons and levers
  56.      * probably isn't important - it will get sorted out when the map is
  57.      * re-loaded.  As such, just exit this function if that is the case.
  58.      */
  59.     
  60.     if (QUERY_FLAG(ol->ob, FLAG_FREED)) return;
  61.     tmp = ol->ob;
  62.     switch(tmp->type) {
  63.     case GATE:
  64.     case HOLE:
  65.       tmp->value=tmp->stats.maxsp?!op->value:op->value;
  66.       tmp->speed=0.5;
  67.       update_ob_speed(tmp);
  68.       break;
  69.     case HANDLE:
  70.     tmp->face = &new_faces[tmp->arch->faces
  71.         [tmp->value=tmp->stats.maxsp?!op->value:op->value]];
  72.       update_object(tmp);
  73.       break;
  74.     case SIGN:
  75.     (*info_map_func)(NDI_UNIQUE | NDI_NAVY,tmp->map,tmp->msg);
  76.     break;
  77.     case ALTAR:
  78.       tmp->value = 1;
  79.       tmp->face = &new_faces[tmp->arch->faces[tmp->value]];
  80.       update_object(tmp);
  81.       break;
  82.     case BUTTON:
  83.     case PEDESTAL:
  84.       tmp->value=op->value;
  85.       tmp->face = &new_faces[tmp->arch->faces[tmp->value]];
  86.       update_object(tmp);
  87.       break;
  88.     case MOOD_FLOOR:
  89.     do_mood_floor(tmp, op);
  90.     break;
  91.     case TIMED_GATE:
  92.     tmp->speed = tmp->arch->clone.speed;
  93.     update_ob_speed(tmp);  /* original values */
  94.         tmp->value = tmp->arch->clone.value;
  95.         tmp->stats.sp = 1;
  96.         tmp->stats.hp = tmp->stats.maxhp;
  97.         break;
  98.     case DIRECTOR:
  99.     case FIREWALL:
  100.         if ((tmp->stats.sp += tmp->stats.maxsp) > 8) /* next direction */
  101.           tmp->stats.sp = ((tmp->stats.sp-1)%8)+1;
  102.         animate_turning(tmp);
  103.         break;
  104.     case TELEPORTER:
  105.     (*move_teleporter_func)(tmp);
  106.     break;
  107.     case CREATOR:
  108.     (*move_creator_func)(tmp);
  109.     break;
  110.     }
  111.   }
  112. }
  113.  
  114. /*
  115.  * Updates everything connected with the button op.
  116.  * After changing the state of a button, this function must be called
  117.  * to make sure that all gates and other buttons connected to the
  118.  * button reacts to the (eventual) change of state.
  119.  */
  120.  
  121. void update_button(object *op) {
  122.   object *ab,*tmp;
  123.   int tot,any_down=0;
  124.   objectlink *ol;
  125.  
  126.   /* LOG(llevDebug, "update_button: %s (%d)\n", op->name, op->count); */
  127.   for (ol = get_button_links(op); ol; ol = ol->next) {
  128.     if (!ol->ob || ol->ob->count != ol->id) {
  129.       LOG(llevError, "Internal error in update_button.\n");
  130.       continue;
  131.     }
  132.     tmp = ol->ob;
  133.     if (tmp->type==BUTTON) {
  134.       for(ab=tmp->above,tot=0;ab!=NULL;ab=ab->above)
  135.         if(!QUERY_FLAG(ab,FLAG_FLYING)) /* Why was nrof removed? */
  136.           tot+=ab->weight*(ab->nrof?ab->nrof:1)+ab->carrying;
  137.       tmp->value=(tot>=tmp->weight)?1:0;
  138.       if(tmp->value)
  139.         any_down=1;
  140.     } else if (tmp->type == PEDESTAL) {
  141.       tmp->value = 0;
  142.       for(ab=tmp->above; ab!=NULL; ab=ab->above) {
  143.         if(ab->head!=NULL)
  144.           ab=ab->head;
  145.         if ( (!QUERY_FLAG(ab,FLAG_FLYING) || QUERY_FLAG(tmp,FLAG_FLY_ON))
  146.        && (ab->race==tmp->slaying ||
  147.              (!strcmp (tmp->slaying, "player") && ab->type == PLAYER)))
  148.           tmp->value = 1;
  149.       }
  150.       if(tmp->value)
  151.         any_down=1;
  152.     }
  153.   }
  154.   if(any_down) /* If any other buttons were down, force this to remain down */
  155.     op->value=1;
  156.   op->face= &new_faces[op->arch->faces[op->value]];
  157.   update_object(op);
  158.   push_button(op); /* Make all other buttons the same */
  159. }
  160.  
  161. /*
  162.  * Updates every button on the map (by calling update_button() for them).
  163.  */
  164.  
  165. void update_buttons(mapstruct *m) {
  166.   objectlink *ol;
  167.   oblinkpt *obp;
  168.   for (obp = m->buttons; obp; obp = obp->next)
  169.     for (ol = obp->link; ol; ol = ol->next) {
  170.       if (!ol->ob || ol->ob->count != ol->id) {
  171.         LOG(llevError, "Internal error in update_button.\n");
  172.         continue;
  173.       }
  174.       if (ol->ob->type==BUTTON || ol->ob->type==PEDESTAL)
  175.       {
  176.         update_button(ol->ob);
  177.         break;
  178.       }
  179.     }
  180. }
  181.  
  182. void use_trigger(object *op) 
  183. {
  184.  
  185.     /* Toggle value */
  186.     op->value = !op->value;
  187.     push_button(op);
  188. }
  189.  
  190. /*
  191.  * Note: animate_object should be used instead of this,
  192.  * but it can't handle animations in the 8 directions
  193.  */
  194.  
  195. void animate_turning(object *op) /* only one part objects */
  196. {
  197.     if (++op->state >= op->arch->animations/8)
  198.       op->state=0;
  199.     op->face = &new_faces[op->arch->faces[(op->stats.sp-1) *
  200.                op->arch->animations / 8 + op->state]];
  201.     update_object(op);
  202. }
  203.  
  204. #define ARCH_SACRIFICE(xyz) ((xyz)->slaying)
  205. #define NROF_SACRIFICE(xyz) ((xyz)->stats.food)
  206.  
  207. /* Returns that object if it meets the sacrifice needs of the altar.
  208.  * If no object meets the needs, return NULL.
  209.  * Function put in (0.92.1) so that identify altars won't grab money
  210.  * unnecssarily - we can see if there is sufficient money, see if something
  211.  * needs to be identified, and then remove money if needed. (via check_altar)
  212.  * Check_altar uses this function also.
  213.  */
  214.  
  215. object *check_altar_sacrifice(object *altar)
  216. {
  217.   object *op;
  218.  
  219.   for (op=altar->above; op; op=op->above)
  220.     if (!QUERY_FLAG(op,FLAG_ALIVE) && op->type != PLAYER) {
  221.       if ((ARCH_SACRIFICE(altar) == op->arch->name  ||
  222.           ARCH_SACRIFICE(altar) == op->name ||
  223.       ARCH_SACRIFICE(altar) == op->slaying) 
  224.       && NROF_SACRIFICE(altar) <= (op->nrof?op->nrof:1))
  225.         return op;
  226.       if (!strcmp(ARCH_SACRIFICE(altar), "money") &&
  227.       op->type==MONEY && op->nrof*op->value>=NROF_SACRIFICE(altar))
  228.         return op;
  229.     }
  230.     return NULL;
  231. }
  232.  
  233.  
  234. /*
  235.  * check_altar checks if sacrifice was accepted and removes sacrificed
  236.  * objects. If sacrifice was succeed return 1 else 0  Might be better to
  237.  * call check_altar_sacrifice (above) than depend on the return value,
  238.  * since check_altar will remove the sacrifice also.
  239.  *
  240.  * Don't really like the name of this functin, operate_altar might be better -
  241.  * we aren't really checking it, since it will remove the sacrifice
  242.  */
  243.  
  244. int check_altar (object *altar)
  245. {
  246.   object *op;
  247.  
  248.   if (!altar->slaying || altar->value)
  249.     return 0;
  250.  
  251.   op=check_altar_sacrifice(altar);
  252.   if (!op)
  253.     return 0;
  254.  
  255.   /* get_altar_sacrifice should have already verified that enough money
  256.    * has been dropped.
  257.    */
  258.   if (!strcmp(ARCH_SACRIFICE(altar), "money")) {
  259.     int number=NROF_SACRIFICE(altar)/op->value;
  260.  
  261.     /* Round up any sacrifices.  Altars don't make change either */
  262.     if (NROF_SACRIFICE(altar)%op->value) number++;
  263.     decrease_ob_nr (op, number);
  264.   }
  265.   else
  266.     decrease_ob_nr (op, NROF_SACRIFICE(altar));
  267.  
  268.   for (op = get_map_ob(altar->map,altar->x,altar->y);
  269.        op && op->type != PLAYER; op=op->above);
  270.   if (op && altar->msg)
  271.     (*info_map_func) (NDI_BLACK, op->map, altar->msg);
  272.   return 1;
  273. }
  274.  
  275. void trigger_move (object *op, int state) /* 1 down and 0 up */
  276. {
  277.   op->stats.wc = state;
  278.   op->face=&new_faces[op->arch->faces[op->stats.wc]];
  279.   update_object(op);
  280.   if (state) {
  281.  
  282.     /* Push button now does it all. */
  283.     use_trigger(op);
  284.  
  285.     op->speed =  op->arch->clone.speed;
  286.     update_ob_speed(op);
  287.     op->speed_left = -1;
  288.   } else {
  289.     op->speed = 0;
  290.     update_ob_speed(op);
  291.   }
  292. }
  293.  
  294. void check_trigger (object *op) {
  295.   object *tmp;
  296.   int push = 0, tot = 0;
  297.  
  298.   switch (op->type) {
  299.     case TRIGGER_BUTTON:
  300.     if (op->weight > 0) {
  301.         for(tmp=op->above; tmp; tmp=tmp->above)
  302.           if(!QUERY_FLAG(tmp,FLAG_FLYING))
  303.         tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1) + tmp->carrying;
  304.           if (tot >= op->weight)
  305.             push = 1;
  306.           if (!push || op->stats.wc == 0)
  307.         trigger_move (op, push);
  308.     }
  309.     return;
  310.  
  311.     case TRIGGER_PEDESTAL:
  312.         for(tmp=op->above; tmp; tmp=tmp->above) {
  313.           if(tmp->head!=NULL)
  314.             tmp=tmp->head;
  315.           if ( (!QUERY_FLAG(tmp,FLAG_FLYING) || QUERY_FLAG(op,FLAG_FLY_ON))
  316.          && (tmp->race==op->slaying ||
  317.              (!strcmp (op->slaying, "player") && tmp->type == PLAYER)))
  318.                 push = 1;
  319.     }
  320.         if (!push || op->stats.wc == 0)
  321.           trigger_move(op, push);
  322.         return;
  323.     case TRIGGER_ALTAR:
  324.         if (op->stats.wc  == 0)
  325.           trigger_move (op,  op->speed == 0 ? check_altar(op) : 0);
  326.       return;
  327.  
  328.     case TRIGGER:
  329.         if (op->stats.wc == 0)   /* handle-trigger */
  330.             trigger_move (op, op->speed == 0 ? 1 : 0);
  331.     return;
  332.  
  333.     default:
  334.     LOG(llevDebug, "Unknown trigger type: %s (%d)\n", op->name, op->type);
  335.     
  336.   }
  337. }
  338.  
  339. void add_button_link(object *button, mapstruct *map, int connected) {
  340.   oblinkpt *obp;
  341.   objectlink *ol = get_objectlink();
  342.  
  343.   if (!map) {
  344.     LOG(llevError, "Tried to add button-link without map.\n");
  345.     return;
  346.   }
  347.  
  348.   SET_FLAG(button,FLAG_IS_LINKED);
  349.  
  350.   ol->ob = button;
  351.   ol->id = button->count;
  352.  
  353.   for (obp = map->buttons; obp && obp->value != connected; obp = obp->next);
  354.  
  355.   if (obp) {
  356.     ol->next = obp->link;
  357.     obp->link = ol;
  358.   } else {
  359.     obp = get_objectlinkpt();
  360.     obp->value = connected;
  361.  
  362.     obp->next = map->buttons;
  363.     map->buttons = obp;
  364.     obp->link = ol;
  365.   }
  366. }
  367.  
  368. /*
  369.  * Remove the object from the linked lists of buttons in the map.
  370.  * This is only needed by editors.
  371.  */
  372.  
  373. void remove_button_link(object *op) {
  374.   oblinkpt *obp;
  375.   objectlink **olp, *ol;
  376.  
  377.   if (op->map == NULL) {
  378.     LOG(llevError, "remove_button_link() in object without map.\n");
  379.     return;
  380.   }
  381.   if (!QUERY_FLAG(op,FLAG_IS_LINKED)) {
  382.     LOG(llevError, "remove_button_linked() in unlinked object.\n");
  383.     return;
  384.   }
  385.   for (obp = op->map->buttons; obp; obp = obp->next)
  386.     for (olp = &obp->link; (ol = *olp); olp = &ol->next)
  387.       if (ol->ob == op) {
  388. /*        LOG(llevDebug, "Removed link %d in button %s and map %s.\n",
  389.            obp->value, op->name, op->map->path);
  390. */
  391.         *olp = ol->next;
  392.         free(ol);
  393.         return;
  394.       }
  395.   LOG(llevError, "remove_button_linked(): couldn't find object.\n");
  396.   CLEAR_FLAG(op,FLAG_IS_LINKED);
  397. }
  398.   
  399. /*
  400.  * Return the first objectlink in the objects linked to this one
  401.  */
  402.  
  403. objectlink *get_button_links(object *button) {
  404.   oblinkpt *obp;
  405.   objectlink *ol;
  406.  
  407.   if (!button->map)
  408.     return NULL;
  409.   for (obp = button->map->buttons; obp; obp = obp->next)
  410.     for (ol = obp->link; ol; ol = ol->next)
  411.       if (ol->ob == button && ol->id == button->count)
  412.         return obp->link;
  413.   return NULL;
  414. }
  415.  
  416. /*
  417.  * Made as a separate function to increase efficiency
  418.  */
  419.  
  420. int get_button_value(object *button) {
  421.   oblinkpt *obp;
  422.   objectlink *ol;
  423.  
  424.   if (!button->map)
  425.     return 0;
  426.   for (obp = button->map->buttons; obp; obp = obp->next)
  427.     for (ol = obp->link; ol; ol = ol->next)
  428.       if (ol->ob == button && ol->id == button->count)
  429.         return obp->value;
  430.   return 0;
  431. }
  432.  
  433. /* This routine makes monsters who are  
  434.  * standing on the 'mood floor' change their
  435.  * disposition if it is different.  
  436.  * If floor is to be triggered must have
  437.  * a speed of zero (default is 1 for all
  438.  * but the charm floor type).
  439.  * by b.t. thomas@nomad.astro.psu.edu
  440.  */
  441.  
  442. void do_mood_floor(object *op, object *op2) {
  443.         object *tmp;
  444.         object *tmp2;
  445.  
  446.         for(tmp=get_map_ob(op->map,op->x,op->y);
  447.                 tmp&&!QUERY_FLAG(tmp,FLAG_MONSTER);tmp=tmp->above)  
  448.             if(tmp->above==NULL) break;
  449.  
  450.         if(tmp->type==PLAYER) return;
  451.  
  452.     switch(op->last_sp) { 
  453.       case 0:            /* furious--make all monsters mad */ 
  454.         if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
  455.             CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
  456.         if(QUERY_FLAG(tmp, FLAG_FRIENDLY)) { 
  457.             tmp->owner = 0;
  458.             CLEAR_FLAG(tmp, FLAG_FRIENDLY);
  459.             tmp->move_type = 0;
  460.         }
  461.         break;
  462.       case 1:             /* angry -- get neutral monsters mad */     
  463.             if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)&&
  464.            !QUERY_FLAG(tmp, FLAG_FRIENDLY))    
  465.                     CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
  466.         break;
  467.       case 2:            /* calm -- pacify unfriendly monsters */ 
  468.             if(!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)) 
  469.             SET_FLAG(tmp, FLAG_UNAGGRESSIVE);        
  470.         break;
  471.       case 3:            /* make all monsters fall asleep */ 
  472.         if(!QUERY_FLAG(tmp, FLAG_SLEEP))
  473.             SET_FLAG(tmp, FLAG_SLEEP);
  474.         break;
  475.       case 4:            /* charm all monsters */
  476.  
  477.         if(op == op2) break;          /* only if 'connected' */ 
  478.         
  479.         for(tmp2=get_map_ob(op2->map,op2->x,op2->y); /* finding an owner */ 
  480.                    tmp2->type!=PLAYER;tmp2=tmp2->above)    
  481.                     if(tmp2->above==NULL) break;
  482.         
  483.         set_owner(tmp,tmp2);
  484.         SET_FLAG(tmp,FLAG_MONSTER);
  485.         tmp->stats.exp = 0;
  486.         SET_FLAG(tmp, FLAG_FRIENDLY);
  487.         tmp->move_type = PETMOVE;
  488.         break;        
  489.  
  490.       default:
  491.         break;
  492.  
  493.     }
  494.         return;
  495. }
  496.  
  497. /* check_inv(), a function to search the inventory, 
  498.  * of a player and then based on a set of conditions,
  499.  * the square will activate connected items. 
  500.  * Monsters can't trigger this square (for now)
  501.  * Values are:    last_sp = 1/0 obj/no obj triggers 
  502.  *         last_heal = 1/0  remove/dont remove obj if triggered
  503.  *        slaying = match object slaying flag
  504.  *         race = match object archetype name flag
  505.  *        hp = match object type (excpt type '0'== PLAYER)
  506.  * -b.t. (thomas@nomad.astro.psu.edu 
  507.  */
  508.  
  509. void check_inv (object *op, object *trig) {
  510. object *tmp;
  511.  
  512.     if(op->type != PLAYER) return;
  513.     if(trig->last_sp) {             /* obj in inv will trigger  */ 
  514.        for(tmp=op->inv; tmp; tmp=tmp->below)
  515.            if((trig->stats.hp && tmp->type == trig->stats.hp) 
  516.            || tmp->slaying == trig->slaying 
  517.            || tmp->arch->name == trig->race) { 
  518.  
  519.            if(trig->last_heal) 
  520.                decrease_ob(tmp);
  521.            use_trigger(trig);
  522.            break;
  523.            } 
  524.     } else {                /* no obj in inv triggers */ 
  525.        for(tmp=op->inv; tmp; tmp=tmp->below)
  526.            if((trig->stats.hp && tmp->type == trig->stats.hp) 
  527.          || tmp->slaying == trig->slaying 
  528.              || tmp->arch->name == trig->race) 
  529.            { 
  530.            break; 
  531.            } else if(!tmp->below) {  
  532.            use_trigger(trig);
  533.            }
  534.     }
  535.     return;
  536. }          
  537.